/*;
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package prototype.communication;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import prototype.communication.events.CommunicationEvent;
import prototype.communication.events.CommunicationEventListener;

/**
 *
 * @author Server
 */
public class Listener implements Runnable {

    private ConnectionResource cr;
    private List<Caster> casters = new ArrayList<Caster>();
    private List<Receiver> receivers = new ArrayList<Receiver>();
    private List<Socket> connections = new ArrayList<Socket>();
    private boolean listening;
    private List<CommunicationEventListener> eventListeners = new ArrayList<CommunicationEventListener>();
    private ServerSocket serverSocket;

    public void run() {

        listening = true;
        listen();
    }

    public Listener(ConnectionResource connRes) {
        cr = connRes;
    }

    private void listen() {
        try {
            serverSocket = new ServerSocket(cr.getPortNo());
        } catch (IOException ex) {
            fireCommunicationEvent(CommunicationEvent.ServerSocketFailed);
            Logger.getLogger(Listener.class.getName()).log(Level.SEVERE, null, ex);
            return;
        }


        while (isListening()) {
            Socket connectionSocket = null;
            try {
                connectionSocket = serverSocket.accept();
            } catch (IOException ex) {
                fireCommunicationEvent(CommunicationEvent.ConnectionLost);
                Logger.getLogger(Listener.class.getName()).log(Level.SEVERE, null, ex);
                continue;
            }
            synchronized (this) {
                try {
                    casters.add(new Caster(connectionSocket.getOutputStream()));
                } catch (IOException ex) {
                    Logger.getLogger(Listener.class.getName()).log(Level.SEVERE, null, ex);
                    continue;
                }
                Receiver rec = null;
                try {
                    rec = new Receiver(connectionSocket.getInputStream());
                } catch (IOException ex) {
                    Logger.getLogger(Listener.class.getName()).log(Level.SEVERE, null, ex);
                    continue;
                }
                (new Thread(rec)).start();
                receivers.add(rec);
                fireCommunicationEvent(CommunicationEvent.ConnectionEstablished);
            }
        }
        try {
            serverSocket.close();
        } catch (IOException ex) {
            Logger.getLogger(Listener.class.getName()).log(Level.SEVERE, null, ex);
        }
        for (Receiver r : receivers) {
            r.stopReceiving();
        }
        for (Socket s : connections) {
            try {
                s.close();
            } catch (IOException ex) {
                Logger.getLogger(Listener.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    public boolean isListening() {
        return listening;
    }

    public void stopListening() {
        listening = false;
        try {
            if (serverSocket != null) {
                serverSocket.close();
            }
        } catch (IOException ex) {
            fireCommunicationEvent(CommunicationEvent.ConnectionLost);
            Logger.getLogger(Listener.class.getName()).log(Level.SEVERE, null, ex);
        }
        Message m = new Message(Message.KICK);
        for (Caster c : casters) {
            if (c != null) {
                c.castMessage(m);
            }
        }
        for (Receiver r : receivers) {
            if (r != null) {
                r.stopReceiving();
            }
        }
        for (Socket s : connections) {
            try {
                s.close();
            } catch (IOException ex) {
                Logger.getLogger(Listener.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public int getCastersSize() {
        return casters.size();
    }

    public int getReceiversSize() {
        return receivers.size();
    }

    public Caster getCasterAt(int i) {
        synchronized (this) {
            return casters.get(i);
        }
    }

    public Receiver getReceiverAt(int i) {
        synchronized (this) {
            return receivers.get(i);
        }
    }

    public int reciverAt(Object rec) {
        return receivers.indexOf(rec);
    }

    public void stopConnectionAt(int i) {
        synchronized (this) {
            receivers.get(i).stopReceiving();
            receivers.remove(i);
            casters.remove(i);
        }
    }

    public synchronized void addEventListener(CommunicationEventListener l) {
        eventListeners.add(l);
    }

    public synchronized void removeEventListener(CommunicationEventListener l) {
        eventListeners.remove(l);
    }

    private synchronized void fireCommunicationEvent(int eventType) {
        CommunicationEvent event = new CommunicationEvent(this, eventType);
        Iterator<CommunicationEventListener> listeners = eventListeners.iterator();
        while (listeners.hasNext()) {
            listeners.next().communicationEventReceived(event);
        }
    }
}
